﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;

namespace MadLibrary.Net.Client
{
    /// <summary>
    /// Specifies size type.
    /// </summary>
    public enum SizeType
    { 
        /// <summary>
        /// Means one byte.
        /// </summary>
        Data = 1,
        /// <summary>
        /// Means one character of string.
        /// </summary>
        String = 1
    }
    /// <summary>
    /// Communicates with server by sending and receiving methods.
    /// </summary>
    public class ServerRequest
    {
        /// <summary>
        /// Specifies target of current request.
        /// </summary>
        public IPEndPoint Target { get; set; }
        /// <summary>
        /// Represents client for communication with server.
        /// </summary>
        private Socket client { get; set; }
        private DateTime startingTime { get; set; }
        /// <summary>
        /// Represents time elapsed since the request connected the server.
        /// </summary>
        public TimeSpan ElapsedTime 
        { 
            get 
            {
                return this.startingTime - DateTime.Now;
            }
        }
        /// <summary>
        /// Represents count of sent data.
        /// </summary>
        public int DataSent { get; private set; }
        /// <summary>
        /// Sets values of properties to their default value by type and creates new ServerRequest object instance.
        /// </summary>
        public ServerRequest()
        {
            this.Target = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 80);
            this.client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this.DataSent = 0;
        }
        /// <summary>
        /// Sets properties to their default values and sets request target.
        /// </summary>
        /// <param name="ep">Specifies request target.</param>
        public ServerRequest(IPEndPoint ep)
        {
            this.Target = ep;
            this.client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this.DataSent = 0;
        }
        /// <summary>
        /// Connects to the server and starts timing for ElapsedTime property.
        /// </summary>
        public void Connect()
        {
            this.client.Connect(this.Target);
            this.startingTime = DateTime.Now;
        }
        /// <summary>
        /// Sends specified string data to server in ASCII encoding.
        /// </summary>
        /// <param name="Request">Specifies string data which has to be sent.</param>
        public void Send(string Request)
        {
            if (!this.client.Connected)
                throw new Exception("Not connected to client.");
            this.DataSent += this.client.Send(Encoding.ASCII.GetBytes(Request));
        }
        /// <summary>
        /// Sends specified string data to server.
        /// </summary>
        /// <param name="Request">Specifies string data which has to be sent.</param>
        /// <param name="e">Specifies strings' encoding.</param>
        public void Send(string Request, Encoding e)
        {
            if (!this.client.Connected)
                throw new Exception("Not connected to client.");
            this.DataSent += this.client.Send(e.GetBytes(Request));
        }
        /// <summary>
        /// Sends specified string data to server in ASCII encoding.
        /// </summary>
        /// <param name="data">Specifies string data which has to be sent.</param>
        public void Send(byte[] data)
        {
            this.DataSent += this.client.Send(data);
        }
        /// <summary>
        /// Receives specified count of data.
        /// </summary>
        /// <param name="Size">Specifies count of data which has to be received.</param>
        /// <param name="t">Specifies SizeType enum instance for size type.</param>
        /// <param name="e">Specifies encoding of string which has to be returned.</param>
        /// <returns>Received string.</returns>
        public string Receive(int Size, SizeType t, Encoding e)
        {
            byte[] data = new byte[Size * (int)t];
            this.client.Receive(data);
            return e.GetString(data);
        }
        /// <summary>
        /// Receives specified count of data.
        /// </summary>
        /// <param name="Size">Specifies count of data which has to be received.</param>
        /// <param name="t">Specifies SizeType enum instance for size type.</param>
        /// <returns>Received data.</returns>
        public byte[] Receive(int Size, SizeType t)
        {
            byte[] data = new byte[Size * (int)t];
            this.client.Receive(data);
            return data;
        }
        /// <summary>
        /// Receives specified count of data.
        /// </summary>
        /// <param name="Size">Specifies count of data (in bytes) which has to be received.</param>
        /// <returns>Received data.</returns>
        public byte[] Receive(int Size)
        {
            byte[] data = new byte[Size];
            this.client.Receive(data);
            return data;
        }
        /// <summary>
        /// Receives 1024 bytes (1 kilobyte).
        /// </summary>
        /// <returns>One received kilobyte</returns>
        public byte[] Receive()
        {
            byte[] data = new byte[1024];
            this.client.Receive(data);
            return data;
        }
        /// <summary>
        /// Discontenctes this request.
        /// </summary>
        public void Disconnect()
        {
            this.client.Disconnect(false);
        }
    }
}
